/*
 *  Copyright 2006 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.mybatis.generator.internal.rules;

import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.IntrospectedTable.TargetRuntime;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.internal.util.StringUtility;

/**
 * This class centralizes all the rules related to code generation - including the methods and objects to create, and certain attributes related to those objects.
 * 
 * @author Jeff Butler
 */
public abstract class BaseRules implements Rules
{
    protected TableConfiguration tableConfiguration;
    
    protected IntrospectedTable introspectedTable;
    
    protected final boolean isModelOnly;
    
    /**
     * 
     */
    public BaseRules(IntrospectedTable introspectedTable)
    {
        super();
        this.introspectedTable = introspectedTable;
        this.tableConfiguration = introspectedTable.getTableConfiguration();
        String modelOnly = tableConfiguration.getProperty(PropertyRegistry.TABLE_MODEL_ONLY);
        isModelOnly = StringUtility.isTrue(modelOnly);
    }
    
    /**
     * Implements the rule for generating the insert SQL Map element and DAO method. If the insert statement is allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateInsert()
    {
        if (isModelOnly)
        {
            return false;
        }
        return tableConfiguration.isInsertStatementEnabled();
    }
    
    /**
     * Implements the rule for generating the insert selective SQL Map element and DAO method. If the insert statement is allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateInsertSelective()
    {
        if (isModelOnly)
        {
            return false;
        }
        return tableConfiguration.isInsertStatementEnabled();
    }
    
    /**
     * Calculates the class that contains all fields. This class is used as the insert statement parameter, as well as the returned value from the select by primary key method. The actual class
     * depends on how the domain model is generated.
     * 
     * @return the type of the class that holds all fields
     */
    @Override
    public FullyQualifiedJavaType calculateAllFieldsClass()
    {
        String answer;
        if (generateRecordWithBLOBsClass())
        {
            answer = introspectedTable.getRecordWithBLOBsType();
        }
        else if (generateBaseRecordClass())
        {
            answer = introspectedTable.getBaseRecordType();
        }
        else
        {
            answer = introspectedTable.getPrimaryKeyType();
        }
        return new FullyQualifiedJavaType(answer);
    }
    
    /**
     * Implements the rule for generating the update by primary key without BLOBs SQL Map element and DAO method. If the table has a primary key as well as other non-BLOB fields, and the
     * updateByPrimaryKey statement is allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateUpdateByPrimaryKeyWithoutBLOBs()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isUpdateByPrimaryKeyStatementEnabled() && introspectedTable.hasPrimaryKeyColumns() && introspectedTable.hasBaseColumns();
        return rc;
    }
    
    /**
     * Implements the rule for generating the update by primary key with BLOBs SQL Map element and DAO method. If the table has a primary key as well as other BLOB fields, and the updateByPrimaryKey
     * statement is allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateUpdateByPrimaryKeyWithBLOBs()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isUpdateByPrimaryKeyStatementEnabled() && introspectedTable.hasPrimaryKeyColumns() && introspectedTable.hasBLOBColumns();
        return rc;
    }
    
    /**
     * Implements the rule for generating the update by primary key selective SQL Map element and DAO method. If the table has a primary key as well as other fields, and the updateByPrimaryKey
     * statement is allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateUpdateByPrimaryKeySelective()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc =
            tableConfiguration.isUpdateByPrimaryKeyStatementEnabled() && introspectedTable.hasPrimaryKeyColumns() && (introspectedTable.hasBLOBColumns() || introspectedTable.hasBaseColumns());
        return rc;
    }
    
    /**
     * Implements the rule for generating the delete by primary key SQL Map element and DAO method. If the table has a primary key, and the deleteByPrimaryKey statement is allowed, then generate the
     * element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateDeleteByPrimaryKey()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isDeleteByPrimaryKeyStatementEnabled() && introspectedTable.hasPrimaryKeyColumns();
        return rc;
    }
    
    /**
     * Implements the rule for generating the delete by example SQL Map element and DAO method. If the deleteByExample statement is allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateDeleteByExample()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isDeleteByExampleStatementEnabled();
        return rc;
    }
    
    /**
     * Implements the rule for generating the result map without BLOBs. If either select method is allowed, then generate the result map.
     * 
     * @return true if the result map should be generated
     */
    @Override
    public boolean generateBaseResultMap()
    {
        if (isModelOnly)
        {
            return true;
        }
        boolean rc = tableConfiguration.isSelectByExampleStatementEnabled() || tableConfiguration.isSelectByPrimaryKeyStatementEnabled();
        return rc;
    }
    
    /**
     * Implements the rule for generating the result map with BLOBs. If the table has BLOB columns, and either select method is allowed, then generate the result map.
     * 
     * @return true if the result map should be generated
     */
    @Override
    public boolean generateResultMapWithBLOBs()
    {
        boolean rc;
        if (introspectedTable.hasBLOBColumns())
        {
            if (isModelOnly)
            {
                rc = true;
            }
            else
            {
                rc = tableConfiguration.isSelectByExampleStatementEnabled() || tableConfiguration.isSelectByPrimaryKeyStatementEnabled();
            }
        }
        else
        {
            rc = false;
        }
        return rc;
    }
    
    /**
     * Implements the rule for generating the SQL example where clause element.
     * 
     * In iBATIS2, generate the element if the selectByExample, deleteByExample, updateByExample, or countByExample statements are allowed.
     * 
     * In MyBatis3, generate the element if the selectByExample, deleteByExample, or countByExample statements are allowed.
     * 
     * @return true if the SQL where clause element should be generated
     */
    @Override
    public boolean generateSQLExampleWhereClause()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isSelectByExampleStatementEnabled() || tableConfiguration.isDeleteByExampleStatementEnabled() || tableConfiguration.isCountByExampleStatementEnabled();
        if (introspectedTable.getTargetRuntime() == TargetRuntime.IBATIS2)
        {
            rc |= tableConfiguration.isUpdateByExampleStatementEnabled();
        }
        return rc;
    }
    
    /**
     * Implements the rule for generating the SQL example where clause element specifically for use in the update by example methods.
     * 
     * In iBATIS2, do not generate the element.
     * 
     * In MyBatis3, generate the element if the updateByExample statements are allowed.
     * 
     * @return true if the SQL where clause element should be generated
     */
    @Override
    public boolean generateMyBatis3UpdateByExampleWhereClause()
    {
        if (isModelOnly)
        {
            return false;
        }
        return introspectedTable.getTargetRuntime() == TargetRuntime.MYBATIS3 && tableConfiguration.isUpdateByExampleStatementEnabled();
    }
    
    /**
     * Implements the rule for generating the select by primary key SQL Map element and DAO method. If the table has a primary key as well as other fields, and the selectByPrimaryKey statement is
     * allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateSelectByPrimaryKey()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc =
            tableConfiguration.isSelectByPrimaryKeyStatementEnabled() && introspectedTable.hasPrimaryKeyColumns() && (introspectedTable.hasBaseColumns() || introspectedTable.hasBLOBColumns());
        return rc;
    }
    
    /**
     * Implements the rule for generating the select by example without BLOBs SQL Map element and DAO method. If the selectByExample statement is allowed, then generate the element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateSelectByExampleWithoutBLOBs()
    {
        if (isModelOnly)
        {
            return false;
        }
        return tableConfiguration.isSelectByExampleStatementEnabled();
    }
    
    /**
     * Implements the rule for generating the select by example with BLOBs SQL Map element and DAO method. If the table has BLOB fields and the selectByExample statement is allowed, then generate the
     * element and method.
     * 
     * @return true if the element and method should be generated
     */
    @Override
    public boolean generateSelectByExampleWithBLOBs()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isSelectByExampleStatementEnabled() && introspectedTable.hasBLOBColumns();
        return rc;
    }
    
    /**
     * Implements the rule for generating an example class. The class should be generated if the selectByExample or deleteByExample or countByExample methods are allowed.
     * 
     * @return true if the example class should be generated
     */
    @Override
    public boolean generateExampleClass()
    {
        if (introspectedTable.getContext().getSqlMapGeneratorConfiguration() == null && introspectedTable.getContext().getJavaClientGeneratorConfiguration() == null)
        {
            // this is a model only context - don't generate the example class
            return false;
        }
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isSelectByExampleStatementEnabled() || tableConfiguration.isDeleteByExampleStatementEnabled() || tableConfiguration.isCountByExampleStatementEnabled()
            || tableConfiguration.isUpdateByExampleStatementEnabled();
        return rc;
    }
    
    @Override
    public boolean generateCountByExample()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isCountByExampleStatementEnabled();
        return rc;
    }
    
    @Override
    public boolean generateUpdateByExampleSelective()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isUpdateByExampleStatementEnabled();
        return rc;
    }
    
    @Override
    public boolean generateUpdateByExampleWithoutBLOBs()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isUpdateByExampleStatementEnabled() && (introspectedTable.hasPrimaryKeyColumns() || introspectedTable.hasBaseColumns());
        return rc;
    }
    
    @Override
    public boolean generateUpdateByExampleWithBLOBs()
    {
        if (isModelOnly)
        {
            return false;
        }
        boolean rc = tableConfiguration.isUpdateByExampleStatementEnabled() && introspectedTable.hasBLOBColumns();
        return rc;
    }
    
    @Override
    public IntrospectedTable getIntrospectedTable()
    {
        return introspectedTable;
    }
    
    @Override
    public boolean generateBaseColumnList()
    {
        if (isModelOnly)
        {
            return false;
        }
        return generateSelectByPrimaryKey() || generateSelectByExampleWithoutBLOBs();
    }
    
    @Override
    public boolean generateBlobColumnList()
    {
        if (isModelOnly)
        {
            return false;
        }
        return introspectedTable.hasBLOBColumns() && (tableConfiguration.isSelectByExampleStatementEnabled() || tableConfiguration.isSelectByPrimaryKeyStatementEnabled());
    }
    
    @Override
    public boolean generateJavaClient()
    {
        return !isModelOnly;
    }
}
